#pragma once

#include <vex/core/handle_policy.h>
#include <vex/core/as_any.h>
#include <vex/core/as_intrusive.h>
#include <vex/functional/callable_with_invoke_policy.h>
#include <vex/functional/contract/multicast_delegate.h>
#include <vex/functional/any_delegate.h>
#include <type_traits>

namespace vex { 
    namespace functional {

        template <class TSignature, class TRet = typename result<TSignature>::type>
        struct any_multicast_delegate_traits {
            typedef contract::multicast_delegate<TSignature, TRet> contract_type;
            typedef boost::intrusive_ptr<contract_type> holder_type;
            typedef contract_type* item_type;
            // required by callable_with_invoke_policy
            typedef typename TRet result_type;
        };

        template<class TSignature, class TRet = typename result<TSignature>::type>
        class any_multicast_delegate : public core::handle_policy<
            any_multicast_delegate<TSignature, TRet>,
            any_multicast_delegate_traits<TSignature, TRet>,
            callable_with_invoke_policy<
                any_multicast_delegate<TSignature, TRet>, 
                any_multicast_delegate_traits<TSignature, TRet> 
            >
        >
        {
        public:
            VEX_ANY_HANDLE_DEFAULT_BODY_IMPL(
                any_multicast_delegate, 
                typename BOOST_IDENTITY_TYPE((any_multicast_delegate_traits<TSignature, TRet>)),
                VEX_TRAITS_IS_DEPENDENT_NAME);
        private:
            holder_type m_holder;
        };

        /// adl support, must be in the same namespace as contract
        namespace contract {
            template<class TSignature, class TRet> auto adl_as_any_from_out(
                multicast_delegate<TSignature, TRet>* p_handle)
                -> any_multicast_delegate<TSignature, TRet>
            {
                return any_multicast_delegate<TSignature, TRet>(p_handle, false);
            }

            template<class TSignature, class TRet> auto as_any_from_in(
                multicast_delegate<TSignature, TRet>* p_handle)
                -> any_multicast_delegate<TSignature, TRet>
            {
                return any_multicast_delegate<TSignature, TRet>(p_handle, true);
            }
        }
    }

    using functional::any_multicast_delegate;
}